<?php

namespace App\Service\Cruise\Disinfect;

use App\Enums\Common\NsqEnum;
use App\Models\Clickhouse\Guanyuan\GyDailyDeliveryReportCHModel;
use App\Models\Common\FsCompanyModel;
use App\Models\Common\FsCompanyUserMappingModel;
use App\Models\Cruise\CruiseCheckTaskGroupModel;
use App\Models\Kms\User\KmsUser;
use App\Models\Shop\ShopModel;
use App\Remote\Rms\RmsUserRemote;
use App\Service\BaseService;
use App\Service\Cruise\Check\CruiseCheckSpecialService;
use Carbon\Carbon;
use GuzzleHttp\Exception\GuzzleException;
use Kuafu\Nsq\NsqPublisher;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Tinderbox\Clickhouse\Exceptions\ClientException;

class CruiseDisinfectService
{
    /**
     * 获取文件内容
     * @throws GuzzleException|ClientException
     */
    public static function analyzeXlsx($file): array
    {
        $spreadsheet = IOFactory::load($file);

        // 第一行校验
        $firstRowValidate = [
            'A' => '服务出库单号',
            'B' => '门店编号',
            'C' => '门店名称',
            'D' => '任务模式',
            'E' => '巡检人角色',
            'F' => '巡检人组织',
            'G' => '巡检人手机',
        ];

        // 列范围
        $cellColumnRadius = array_keys($firstRowValidate);

        // 获得数据
        $data = [];

        // 遍历第一个sheet中的所有单元格
        foreach ($spreadsheet->getWorksheetIterator() as $worksheet) {
            // 循环每一行
            foreach ($worksheet->getRowIterator() as $row) {
                // 第一行校验
                if ($row->getRowIndex() == 1) {
                    $firstRowValidateRes = [];
                    foreach ($row->getCellIterator() as $cell) {
                        if (in_array($cell->getColumn(), $cellColumnRadius)) {
                            if ($firstRowValidate[$cell->getColumn()] != $cell->getValue()) {
                                $firstRowValidateRes[] = $cell->getColumn() . '列应为 [' . $firstRowValidate[$cell->getColumn()] . '] 文件是 [' . $cell->getValue() . ']';
                            }
                        }
                    }
                    // 抛出错误
                    if (count($firstRowValidateRes)) {
                        BaseService::throwErr(implode(',', $firstRowValidateRes), 400);
                    }
                } else {
                    // 组数据

                    // 列的key
                    $columnDataKey = [
                        'A' => 'serviceNo',
                        'B' => 'shopCode',
                        'C' => 'shopName',
                        'D' => 'testType',
                        'E' => 'role',
                        'F' => 'organize',
                        'G' => 'mobile',
                    ];
                    foreach ($row->getCellIterator() as $cell) {
                        if ($cell->getColumn() == 'B' && $cell->getValue() == null) {
                            unset($data[$row->getRowIndex()]);
                            continue 2;
                        }

                        if (in_array($cell->getColumn(), $cellColumnRadius)) {
                            $data[$row->getRowIndex()][$columnDataKey[$cell->getColumn()]] = $columnDataKey[$cell->getColumn()] == 'shopCode' ? (int)$cell->getValue() : (string)$cell->getValue();
                        }
                    }
                }
            }
            break; // 只要第一个
        }

        // 检验所有数据
        $checkErrorData = self::checkTaskListDataEffective(array_values($data));
        if ($checkErrorData['checkResult'] !== true) {
            BaseService::throwErr($checkErrorData['checkError'], 400);
        }

        // 已开业的门店code
        $shopBusinessCHModelCollection = GyDailyDeliveryReportCHModel::query()
            ->where("date", ">", Carbon::now()->subDays(30)->toDateString())
            ->whereIn('storeCode', collect($data)->pluck('shopCode')->unique()->values()->toArray())
            ->select(['storeCode'])
            ->get();
        $shopBusinessList              = collect($shopBusinessCHModelCollection)->pluck('storeCode')->toArray();

        return collect($checkErrorData['taskList'])->map(function ($item) use ($shopBusinessList) {
            $item['businessStatus'] = (isTest() || !empty($shopBusinessList) && in_array($item['shopCode'], $shopBusinessList));
            return $item;
        })->values()->toArray();
    }


    /**
     * 添加导入消杀任务
     * @param array $taskList
     * @param int $templateId
     * @param string $startDateTime
     * @param string $endDateTime
     * @param string $name
     * @return array
     * @throws GuzzleException
     * @throws \Exception
     */
    public static function addImportTask(
        array  $taskList,
        int    $templateId,
        string $startDateTime,
        string $endDateTime,
        string $name
    ): array {
        // 检验所有数据
        $checkErrorData = self::checkTaskListDataEffective($taskList);
        if ($checkErrorData['checkResult'] !== true) {
            BaseService::throwErr($checkErrorData['checkError'], 400);
        }

        $groups = [];
        foreach ($checkErrorData['taskList'] as $item) {
            // 过滤不营业的订单
            if (!$item['businessStatus']) {
                continue;
            }

            $data = [
                'templateId'   => $templateId,
                'shopId'       => $item['shopId'],
                'relationCode' => $item['serviceNo'] ?? '',
            ];
            // 发送NSQ
            CruiseDisinfectService::aboutCruiseOrderChangedPublishNsq('created', $data);

            // 如果有用户 则直接是归属
            if ((int)$item['userId']) {
                $data['userId'] = (int)$item['userId'];
                // 发送NSQ
                CruiseDisinfectService::aboutCruiseOrderChangedPublishNsq('belongedTo', $data);
            }

            $groups[] = [
                'role'         => (int)$item['roleId'],
                'source'       => CruiseCheckTaskGroupModel::SOURCE_IMPORT,
                'userId'       => (int)$item['userId'],
                'companyId'    => (string)$item['companyId'],
                'shopIds'      => [$item['shopId']],
                'relationCode' => $item['serviceNo'] ?? '',
            ];
        }

        // 添加任务组
        $id = CruiseCheckSpecialService::add($name, Carbon::parse($startDateTime)->startOfDay()->timestamp, Carbon::parse($endDateTime)->endOfDay()->timestamp, $templateId, $groups);

        return [
            'id' => $id,
        ];
    }


    /**
     * 校验表单内所有数据
     * @param array $taskList
     * @return array
     * @throws GuzzleException
     */
    public static function checkTaskListDataEffective(array $taskList): array
    {
        // 判断服务商是否重复
        $serverCount = collect($taskList)->pluck('organize')->toArray();

        if (count(array_unique($serverCount)) > 1) {
            return [
                'checkResult' => false,
                'taskList'    => $taskList,
                'checkError'  => '每次只允许一个服务商导入任务',
            ];
        }

        $errorData    = [];
        $mobileList   = [];
        $shopCodes    = [];
        $companyList  = [];
        $kfUserMobile = [];
        $serviceNoArr = [];

        try {
            // 校验所有手机号
            $pattern = '/^1\d{10}$/';
            foreach ($taskList as $task) {
                if (strlen($task['mobile'])) {
                    $mobileList[] = $task['mobile'];
                }
                $shopCodes[]  = $task['shopCode'];

                if ($task['organize'] != '夸父') {
                    $companyList[$task['mobile']] = $task['organize'];
                } else {
                    $kfUserMobile[] = $task['mobile'];
                }

                if (strlen($task['serviceNo'])) {
                    if (in_array($task['serviceNo'], $serviceNoArr)) {
                        $errorData[] = '服务单号' . $task['serviceNo'] . '重复了，单号不能重复';
                    }
                    $serviceNoArr[] = $task['serviceNo'];
                }

                if (strlen($task['mobile']) && !preg_match($pattern, $task['mobile'])) {
                    $errorData[] = $task['mobile'] . '-手机号格式错误';
                }
            }

            // 检验服务单号是否重复
            foreach (array_chunk($serviceNoArr, 1000) as $serviceNoItem) {
                $count = CruiseCheckTaskGroupModel::query()
                    ->whereIn('relationCode', $serviceNoItem)
                    ->count();
                if ($count) {
                    $errorData[] = '服务单号禁止重复提交，请核实后重试';
                }
            }

            if (!empty($errorData)) {
                return [
                    'checkResult' => false,
                    'taskList'    => $taskList,
                    'checkError'  => implode(',', $errorData),
                ];
            }


            // 通过手机号获得用户ID 调rms接口
            $userInfo = [];
            if (count($mobileList)) {
                $userInfo = RmsUserRemote::getUserByMobiles($mobileList);
            }
            $rmsInfo  = [];
            collect($userInfo)->map(function ($item) use (&$rmsInfo) {
                $rmsInfo[$item['mobile']] = [
                    'userId'   => $item['id'],
                    'username' => $item['username'],
                ];
            });

            // 夸父用户
            $kfUserInfo = KmsUser::query()
                ->whereIn('mobile', $kfUserMobile)
                ->pluck('id', 'mobile')
                ->toArray();

            // $rmsId = KmsUser::query()
            //     ->whereIn('mobile', $mobileList)
            //     ->pluck('rmsUserId', 'mobile')
            //     ->toArray();


            // 巡检机构
            $company = [];
            collect(FsCompanyModel::getCompanyLists())->each(function ($item) use (&$company) {
                $company[$item['departmentId']] = $item['name'];
            });

            // 获得用户当前绑定部门
            $companyIdList = [];

            // companyCount
            $companyCount = [];
            FsCompanyModel::query()
                ->where('organizationId', FsCompanyModel::WEB_ORGANIZATION_ID)
                ->whereIn('name', array_values($companyList))
                ->get()
                ->map(function (FsCompanyModel $companyModel) use ($companyList, &$companyIdList, $rmsInfo, &$companyCount) {
                    $companyCount[$companyModel->name][] = $companyModel->id;
                    $mobile = array_search($companyModel->name, $companyList);
                    if ($mobile && isset($rmsInfo[$mobile])) {
                        $companyIdList[$mobile][] = $companyModel->id;
                    }
                });

            foreach ($companyCount as $companyName => $countItem) {
                if (count($countItem) > 1) {
                    $errorData[] = $companyName . ' 存在多个组织，禁止提交';
                }
            }

            // 用户和部门的关系
            $companyUserList = [];
            FsCompanyUserMappingModel::query()
                ->whereIn('rmsUserId', collect($rmsInfo)->pluck('userId'))
                ->get()
                ->map(function (FsCompanyUserMappingModel $fsCompanyUserMappingModel) use (&$companyUserList) {
                    if (isset($companyUserList[$fsCompanyUserMappingModel->rmsUserId])) {
                        $companyUserList[$fsCompanyUserMappingModel->rmsUserId][] = $fsCompanyUserMappingModel->fsCompanyId;
                    } else {
                        $companyUserList[$fsCompanyUserMappingModel->rmsUserId] = [$fsCompanyUserMappingModel->fsCompanyId];
                    }
                });

            if (!empty($errorData)) {
                return [
                    'checkResult' => false,
                    'taskList'    => $taskList,
                    'checkError'  => implode(',', $errorData),
                ];
            }

            // 通过shopCode获得门店详情
            $shopInfo = [];
            ShopModel::query()
                ->whereIn('shopCode', $shopCodes)
                ->select(['id', 'shopCode', 'name', 'addressPrefix'])
                ->get()
                ->map(function (ShopModel $shopModel) use (&$shopInfo) {
                    $shopInfo[$shopModel->shopCode] = [
                        'shopId'   => $shopModel->id,
                        'shopCode' => $shopModel->shopCode,
                        'shopName' => $shopModel->name,
                        'address'  => $shopModel->addressPrefix,
                    ];
                })->toArray();

            // 每行数据校验
            foreach ($taskList as $k => $task) {
                $row = $k + 2;
                if (!in_array($task['testType'], ['指派', '领取'])) {
                    $errorData[] = "第{$row}行的巡检人角色应是内部员工或者第三方巡检，导入为{$task['testType']}";
                }
                if (!in_array($task['role'], ['内部员工', '第三方巡检'])) {
                    $errorData[] = "第{$row}行的巡检人角色应是内部员工或者第三方巡检，导入为{$task['role']}";
                }
                if ($task['role'] == '内部员工' && $task['testType'] == '领取') {
                    $errorData[] = "第{$row}行的角色是内部员工，任务模式只能是指派";
                }
                if ($task['testType'] == '领取' && strlen($task['mobile'])) {
                    $errorData[] = "第{$row}行的任务模式是领取，不能填写手机号";
                }
                if ($task['testType'] == '指派' && !strlen($task['mobile'])) {
                    $errorData[] = "第{$row}行的任务模式是指派，没有填写手机号";
                }
                if ($task['role'] == '内部员工' && $task['organize'] != '夸父') {
                    $errorData[] = "第{$row}行的角色是内部员工，巡检人组织只能是夸父";
                }
                if ($task['role'] == '内部员工' && !isset($rmsInfo[$task['mobile']])) {
                    $errorData[] = "第{$row}行的手机号未匹配到有效员工";
                }
                if ($task['role'] == '第三方巡检' && !in_array($task['organize'], $company)) {
                    $errorData[] = "第{$row}行的角色是第三方巡检，未匹配到有效组织";
                }
                if (!isset($shopInfo[$task['shopCode']])) {
                    $errorData[] = "第{$row}行的门店编码不存在";
                }

                if ($task['role'] == '内部员工' && !isset($kfUserInfo[$task['mobile']])) {
                    $errorData[] = "第{$row}行的角色是内部员工，在内部未找到该员工";
                }

                // 填补数据
                $taskList[$k]['shopId']      = $shopInfo[$task['shopCode']]['shopId'] ?? 0;
                $taskList[$k]['shopName']    = $shopInfo[$task['shopCode']]['shopName'] ?? '';
                $taskList[$k]['shopAddress'] = $shopInfo[$task['shopCode']]['address'] ?? '';
                $taskList[$k]['userId']      = $rmsInfo[$task['mobile']]['userId'] ?? 0;
                $taskList[$k]['username']    = $rmsInfo[$task['mobile']]['username'] ?? '';
                $taskList[$k]['companyId']   = (string)array_search($task['organize'], $company);
                $taskList[$k]['roleId']      = array_search($task['role'], CruiseCheckTaskGroupModel::ROLE_TEXT);

                // 部门校验
                if ($task['role'] == '第三方巡检' && $task['testType'] == '指派') {
                    // 用户是否存在
                    if (!isset($rmsInfo[$task['mobile']])) {
                        $errorData[] = "第{$row}行的用户不存在";
                    }

                    // 用户所拥有的组织列表
                    $userCompany = $companyUserList[$taskList[$k]['userId']] ?? [];

                    // 当前组织ID
                    $nowCompanyId = $companyIdList[$task['mobile']] ?? [];

                    if (array_diff($nowCompanyId, $userCompany) != []) {
                        $errorData[] = "第{$row}行的用户与组织不匹配";
                    }
                }

                // 抛出
                if (!empty($errorData)) {
                    return [
                        'checkResult' => false,
                        'taskList'    => $taskList,
                        'checkError'  => implode(',', $errorData),
                    ];
                }
            }

            return [
                'checkResult' => true,
                'taskList'    => $taskList,
                'checkError'  => '',
            ];
        } catch (\Exception $exception) {
            return [
                'checkResult' => false,
                'taskList'    => $taskList,
                'checkError'  => '数据校验错误',
            ];
        }
    }


    /**
     * 关于巡检订单变更发布Nsq
     * @param $status
     * @param $data
     * @return void
     * @throws \Exception
     */
    public static function aboutCruiseOrderChangedPublishNsq($status, $data)
    {
        // 发送NSQ
        (new NsqPublisher(config('nsq.default.address')))
            ->pub(NsqEnum::TOPIC_TIGER_CRUISE_ORDER_CHANGED, ['data' => $data, 'status' => $status]);
    }

}
